#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G06 -- S08 ------------------------------------------------------------##
__all__ = []

from .. import formlib as layout
from .. import helpstrings as hs
from .. import pilelib as stack
from ..forms import klondike as KlondikeLayout
from ..forms import calculate as CalculateLayout
from ..gamelib import info as gi, game
from ..packlib import place
from ..piles import (yukon as YukonStack, multiples as MultiplesStack,
                     numerica as NumericaStack, talon as TalonStack,)
from ..utillib.misc import kwdefault

Wrap = stack.StackWrapper
GT, SL = gi.GT, gi.SL


class One234(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layout.CalculateLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 W, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.InitialDealTalon
        No redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.multiples.MultiRKFoundation (max_move=0)
        Int any 1 card per. Build up in rank alone wrap by rank multiples of:
        1 on 1st, 2 on 2nd, 3 on 3rd, 4 on 4th. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.BasicRowStack
        Init 6 open card per. Build none. Play top.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint
    _row_
      Pile 8 S _foundation. Init deal 6 open card. Build none. Play top.

    :note:
      ⟪highlight_play⟫ disabled
    """
    __info__ = dict(id=0x226, name='1-2-3-4', game_type=(GT.ONE_DECK |
                    GT.OPEN), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = CalculateLayout.CalculateLayout
    _talon_ = stack.InitialDealTalon
    _foundation_ = MultiplesStack.MultiRKFoundation
    _row_ = stack.BasicRowStack
    _hint_ = NumericaStack.Hint

    def _create_game(self, **kw):
        """
        _create_game([rows=8, playcards=20]) -> Layout

        Set canvas help label with foundation build type.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.calculate` for keyword definitions.
        """
        kwdefault(kw, rows=8, playcards=20)
        l, s = super(One234, self)._create_game(**kw), self.s
        self.table_size = (l.size.width + l._get_text_width(hs.FOUND_RK_TEXT),
                           l.size.height)
        self.texts['help'] = l.create_HELP_RK_TEXT(s['foundations'][-1],
                                            text=hs.FOUND_RK_TEXT, anchor='w')
        return l

    def _start(self, rows=6, flip=True, **kw):
        """
        _start([rows=6, flip=True]) -> None

        Deal 1 card per foundation. Deal 6 open card per row.
        """
        self.s['talon'].dealBaseCards(ncards=len(self.s['foundations']))
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()

    @property
    def _highlight_piles(self):
        """
        Get stacks to highlight for possible move.

        :return: empty.
        :rtype: tuple
        """
        return ()


class Calculation(One234):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layout.CalculateLayout
        - row [4 horz S foundation, vert offset]
        - talon [1 W, no offset]
        - waste [1 E talon, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No Redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack (max_cards=1)
        Init 1 card per. Build none. Play top. Max 1 card.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.multiples.Calculation_Foundation (max_move=0)
        Int ace, 2, 3, 4. Build up in rank alone wrap by rank multiples of: 1
        on 1st, 2 on 2nd, 3 on 3rd, 4 on 4th. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.ReserveStack
        Init 1 open card per. Build any. Play top. Max 1 card

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint

    :note:
      ⟪highlight_play⟫ disabled
    """
    __info__ = dict(id=0x100, name='Calculation', game_type=(GT.ONE_DECK |
                    GT.KDE | GT.XPAT), decks=1, skill_level=SL.MOSTLY_SKILL,
                    altnames=('Progression',), version='2.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = Wrap(stack.WasteStack, max_cards=1)
    _foundation_ = MultiplesStack.Calculation_Foundation
    _row_ = stack.ReserveStack

    def _create_game(self, **kw):
        """
        _create_game([rows=4]) -> Layout

        Set canvas help label with foundation build type.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.calculate` for keyword definitions.
        """
        kw.setdefault('rows', 4)
        return super(Calculation, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE, TWO, THREE and FOUR on top by rank for
        _foundation_ deal.
        """
        return place.oneEach(cards, self['ranks'][:4])

    def _start(self, **kw):
        """
        _start([rows=1]) -> None

        Deal 1 card per foundation. Deal 1 open card per row. Deal 1 card per
        waste.
        """
        kw.setdefault('rows', 1)
        super(Calculation, self)._start(**kw)
        self.s['talon'].deal_cards()


class Hopscotch(Calculation):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layout.CalculateLayout
        - row [4 horz S foundation, vert offset]
        - talon [1 W, no offset]
        - waste [1 E talon, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No Redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack (max_cards=1)
        Init 1 card per. Build none. Play top. Max 1 card.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.multiples.Calculation_Foundation (max_move=0)
        Int ace, 2, 3, 4 each of clubs. Build up in rank alone wrap by
        rank multiples of: 1 on 1st, 2 on 2nd, 3 on 3rd, 4 on 4th. Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.ReserveStack
        Init 1 open card per. Build any. Play top. Max 1 card

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint

    :note:
      ⟪highlight_play⟫ disabled
    """
    __info__ = dict(id=0x5e, name='Hopscotch', game_type=(GT.ONE_DECK |
                    GT.GNOME), decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='3.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE, TWO, THREE and FOUR of clubs on top by rank, for
        _foundation_ deal.
        """
        return place.oneEach(cards, self['ranks'][:4], suit=0)


class BetsyRoss(Calculation):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layout.CalculateLayout
        - talon [1 W, no offset]
        - waste [1 E talon, no offset]
        - foundation [8 over 2 row N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No Redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack (max_cards=1)
        Init 1 card per. Build none. Play top. Max 1 card.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.AbstractFoundation (max_cards=1, max_move=0),
          pysollib.stacks.MultiplesStack.MultiRKFoundation (max_cards=12,
          max_move=0)
        Int ace, 2, 3, 4 N, 2, 4, 6, 8 S. Build none N, and S up in rank alone
        wrap by rank multiples of: 1 on 1st, 2 on 2nd, 3 on 3rd, 4 on 4th.
        Play none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint

    :note:
      ⟪highlight_play⟫ disabled
    """
    __info__ = dict(id=0x86, name='Betsy Ross', game_type=GT.ONE_DECK,
                    decks=1, redeals=2, skill_level=SL.MOSTLY_LUCK,
                    altnames=('Fairest', 'Four Kings', 'Musical Patience',
                    'Quadruple Alliance', 'Plus Belle'), version='3.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = Wrap(stack.WasteTalon, max_rounds=2)
    _foundations_ = (Wrap(stack.AbstractFoundation, max_cards=1,
                                 max_move=0),
                    Wrap(MultiplesStack.MultiRKFoundation, max_cards=12,
                                 max_move=0),)

    def _create_game(self, **kw):
        """
        _create_game([rows=0, bases=suit * 2]) -> Layout

        Set canvas help label with foundation build type.

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.calculate` for keyword definitions.
        """
        suit = self['suits']
        kwdefault(kw, rows=0, bases=suit * 2)
        return super(BetsyRoss, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE TWO THREE FOUR TWO FOUR SIX and EIGHT on top by
        rank for _foundation_ deal.
        """
        ranks = self['ranks']
        return place.oneEach(cards, ranks[:4] + ranks[1:8:2])


class Abacus(game.Game):
    """
    Finish 1 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.KlondikeLayout
        - row [7 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - foundation [4 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.DealRowTalon
        Deal 1 card per row on click. No Redeal.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.multiples.MultiSSFoundation (max_move=0)
        Int ace club, 2 heart, 3 spade, 4 diamond. Build one rank up by rank
        multiples of: 1 on 1st, 2 on 2nd, 3 on 3rd, 4 on 4th in same suit.
        Play none.

      _row_ : pysollib.stack.Stack
        .. pysollib.stacks.multiples.MultiYukonSSRowStack
        Init [1..7] card per. Build set rank down by rank multples of: ace
        club, two heart, three spade, four diamond in same suit. Play set
        regardless.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.yukon.Hint

    :note:
      <<highlight_play>> disabled
    """
    __info__ = dict(id=0x509d0, name='Abacus', game_type=(GT.YUKON |
                    GT.XORIGINAL), decks=1, version='4.41',
                    skill_level=SL.BALANCED)
    __author__ = 'Markus Oberhumer'
    __credits__ = ['Randy Rasa']
    __version__ = 7.70

    _layout_ = KlondikeLayout.KlondikeLayout
    _talon_ = stack.DealRowTalon
    _foundation_ = MultiplesStack.MultiSSFoundation
    _row_ = Wrap(MultiplesStack.MultiYukonSSRowStack, mod=13)
    _hint_ = YukonStack.Hint

    def _create_game(self, **kw):
        """
        _create_game([rows=7, playcards=25, texts=True]) -> Layout

        :returns: current layout with piles and their texts set.
        :seealso: `pysollib.layouts.klondike` for keyword definitions.
        """
        kwdefault(kw, rows=7, playcards=25, texts=True)
        return super(Abacus, self)._create_game(**kw)
        #self.table_size = (l.size.width + l._get_text_width(hs.FOUND_SS_TEXT),
        #                   l.size.height,)
        #self.game['texts'].help = l.create_help_text(None, 'sw', dx=l.XM,
        #                       dy=self.height - l.YM, text=hs.FOUND_SS_TEXT)
        #return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE club, TWO heart, THREE spade and FOUR diamond on
        top by rank, for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.SeqSuits)

    def _start(self, flip=True, reverse=True, **kw):
        """
        _start([flip=True, reverse=True]) -> None

        Deal 1 card per foundation. Deal [1..7] card on row.
        """
        self.s['talon'].dealBaseCards(ncards=len(self.s['foundations']))
        for i in range(1, len(self.s['rows'])):
            self.s['talon'].dealRow(rows=self.s['rows'][i:], flip=flip,
                                    frames=0, reverse=reverse)
        self._play_sound('deal')
        self.s['talon'].dealRow(reverse=reverse)

    @property
    def _highlight_piles(self):
        """
        Get stacks to highlight for possible move.

        :return: empty.
        :rtype: tuple
        """
        return ()


class SeniorWrangler(game.Game):
    """
    Finish 2 deck on foundation.

    :CVariables:
      _layout_ : pysollib.layout.Layout
        .. pysollib.layouts.klondike.KlondikeLayout
        - row [8 horz S foundation, vert offset]
        - talon [1 NW, no offset]
        - foundation [8 horz N, no offset]

      _talon_ : pysollib.stack.Stack
        .. pysollib.stacks.talon.SeniorWrangler_Talon
        Redeal 1 row pile each round over all row. 8 Redeal.

      _row_ : pysollib.stack.Stack
        .. pysollib.stack.BasicRowStack
        Init 12 card per. Build none. Play top.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stacks.mulitples.MultiRKFoundation (max_move=0)
        Int ace..8. Build none N, and S up in rank alone
        wrap by rank multiples of: 1 on 1st and 5th, 2 on 2nd and 6th, 3 on
        3rd and 7th, 4 on 4th and 8th. Play none.

      _hint_ : pysollib.hint.AbstractHint
        .. pysollib.stacks.numerica.Hint
    """
    __info__ = dict(id=0x299d, name='Senior Wrangler', game_type=GT.TWO_DECK,
                    decks=2, redeals=8, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = KlondikeLayout.KlondikeLayout
    _talon_ = Wrap(TalonStack.SeniorWrangler_Talon, max_rounds=9)
    _foundation_ = Wrap(MultiplesStack.MultiRKFoundation, max_move=0)
    _row_ = stack.BasicRowStack

    def _create_game(self, **kw):
        """
        _create_game([rows=8, playcards=20]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.klondike` for keyword definitions.
        """
        kwdefault(kw, rows=8, playcards=20)
        return super(SeniorWrangler, self)._create_game(**kw), self.s

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE through Eight on top by rank, for _foundation_
        deal.
        """
        ranks = self['ranks']
        return place.oneEach(cards, ranks[:8])

    def _start(self, rows=12, flip=False, **kw):
        """
        _start([rows=1]) -> None

        Deal 1 card per foundation. Deal 12 card per row.
        """
        self.s['talon'].dealBaseCards(ncards=len(self.s['foundations']))
        for i in range(rows - 1):
            self.s['talon'].dealRow(flip=flip, frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow()


class SPatience(game.Game):
    """
    Finish 2 deck on foundation.

    Layout
      - row [2 horz S talon, vert offset]
      - talon [1 W waste, no offset]
      - waste [1 E, no offset]
      - foundation [13 s-shape N, no offset]

    :CVariables:
      _talon_ : pysollib.stack.Stack
        .. pysollib.stack.WasteTalon
        Deal 1 card on waste on click. No Redeal.

      _waste_ : pysollib.stack.Stack
        .. pysollib.stack.WasteStack (max_cards=1)
        Init 1 card per. Build none. Play top. Max 1 card.

      _reserve_ : pysollib.stack.Stack
        .. pysollib.stack.stack.ReserveStack
        Build any. Play top. Max 1 card.

      _foundation_ : pysollib.stack.Stack
        .. pysollib.stack_RK_Foundation (max_move=0, max_cards=8)
        Int ace..king. Build up in rank alone. Play none. Max 8 card.
    """
    __info__ = dict(id=0x29d0, name='S Patience', game_type=GT.TWO_DECK,
                    decks=2, skill_level=SL.BALANCED,
                    version='1.00fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = Wrap(stack.WasteStack, max_cards=1)
    _foundation_ = Wrap(stack.RK_Foundation, max_cards=8, max_move=0)
    _reserve_ = stack.ReserveStack

    def _create_game(self, reserves=2, **kw):
        """
        _create_game([reserves=2]) -> Layout

        :returns: current layout with piles and their texts set.

        :seealso: `pysollib.layouts.klondike` for keyword definitions.
        :todo: needs seperate layout.
        """
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 15 * l.HXS, l.YM + 4 * l.YS)
        for dx, dy in ((4, 4), (3, 2), (2, 0), (1, 2), (0, 7), (1, 12),
                       (2, 14), (3, 16), (4, 20), (3, 26), (2, 28), (1, 26),
                       (0, 24),):
            x, y = l.delta_coords(l.XM, l.YM, dx, dy, fy=10)
            s['foundations'].append(self._foundation_(x, y, game=self))
        dx, y = self.width - (2 * l.XS), l.YM + l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'nw')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        y += l.YS
        for i in l.col_block_iter(dx, y, reserves, reserves):
            s['reserves'].append(self._reserve_(x, y, game=self))
            l.set_ncards_text(anchor='s')
        l.defaultStackGroups()
        return

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE through KING on top by rank, for _foundation_
        deal.
        """
        return place.oneEach(cards, self['ranks'])  # [7:] + [:7]

    def _start(self):
        """
        _start([rows=1]) -> None

        Deal 1 card per foundation. Deal 1 card per waste.
        """
        self._play_sound('deal')
        self.s['talon'].dealBaseCards(ncards=len(self.s['foundations']))
        self.s['talon'].deal_cards()


PROVIDES = [Calculation, Hopscotch, BetsyRoss, One234, Abacus, SeniorWrangler,
            SPatience]